﻿// Copyright (c) MichaConrad. All rights reserved. See License.txt in the project root for license information.

namespace LinqToLdapExtensions
{
    using System;
    using LinqToLdap;
    using LinqToLdapExtensions.Properties;
    using LinqToLdapExtensions.Scope;

    /// <summary>
    /// A simple unit of work implementation which takes care of bind and unbind.
    /// </summary>
    /// <seealso cref="CurrentDirectoryContext"/>
    /// <seealso cref="IDirectoryContextFactory"/>
    public class DirectoryContextUnitOfWork : ICurrentDirectoryContext
    {
        /// <summary>
        /// The factory.
        /// </summary>
        private readonly IDirectoryContextFactory factory;

        /// <summary>
        /// Initializes a new instance of the <see cref="DirectoryContextUnitOfWork"/> class.
        /// </summary>
        /// <param name="factory">
        /// The factory.
        /// </param>
        /// <exception cref="ArgumentNullException">
        /// If factory is null.
        /// </exception>
        public DirectoryContextUnitOfWork(IDirectoryContextFactory factory)
        {
            if (factory == null)
            {
                throw new ArgumentNullException("factory");
            }

            this.factory = factory;
        }

        /// <summary>
        /// Gets the factory.
        /// </summary>
        public IDirectoryContextFactory Factory
        {
            get
            {
                return this.factory;
            }
        }

        /// <summary>
        /// Gets the current <see cref="IDirectoryContext"/> according to the scoping defined
        /// by this implementation.
        /// </summary>
        /// <remarks>
        /// This getter will fail if there is no <see cref="IDirectoryContext"/> bound to the store.
        /// </remarks>
        public IDirectoryContext CurrentContext
        {
            get
            {
                return this.Factory.GetCurrentContext();
            }
        }

        /// <summary>
        /// Begins a unit of work by creating a <see cref="IDirectoryContext"/> with the factory 
        /// and binding it to <see cref="IDirectoryContextFactory.CurrentDirectoryContextBinding"/>.
        /// </summary>
        /// <exception cref="InvalidOperationException">
        /// If there is already a <see cref="IDirectoryContext"/> bound to <see cref="IDirectoryContextFactory.CurrentDirectoryContextBinding"/>.
        /// </exception>
        public virtual void BeginUnitOfWork()
        {
            if (this.factory.CurrentDirectoryContextBinding.HasBind())
            {
                throw new InvalidOperationException(Resources.UnitOfWorkBindingAlreadyExists);
            }

            var ctx = this.factory.CreateContext();
            this.factory.CurrentDirectoryContextBinding.Bind(ctx);
        }

        /// <summary>
        /// Ends the unit of work and unbinds the <see cref="IDirectoryContext"/> from <see cref="IDirectoryContextFactory.CurrentDirectoryContextBinding"/>.
        /// </summary>
        /// <exception cref="InvalidOperationException">
        /// If there is no <see cref="IDirectoryContext"/> bound to <see cref="IDirectoryContextFactory.CurrentDirectoryContextBinding"/>.
        /// </exception>
        public virtual void EndUnitOfWork()
        {
            if (!this.factory.CurrentDirectoryContextBinding.HasBind())
            {
                throw new InvalidOperationException(Resources.UnitOfWorkBindingDoesNotExist);
            }

            var ctx = this.factory.CurrentDirectoryContextBinding.Unbind();
            ctx.Dispose();
        }
    }
}
